//
//  LinAlg.h
//  Vector_Math
//
//  Created by John R Peterson on 3/10/12.
//  Copyright 2012 __MyCompanyName__. All rights reserved.
//

#ifndef Vector_Math_LinAlg_h
#define Vector_Math_LinAlg_h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>

#define TRUE 1
#define FALSE 0
#define PI 3.14159265
#define BASESIZE 10

typedef struct Vec_struct {
    float *val;
    int size;
    short isColumn;
    short mode; // are the contents being automatically managed, tells free to do the right thing
} Vec;

typedef struct Matrix_struct {
    float **val;
    int rows;
    int columns;
    short mode; // are the contents being automatically managed
} Matrix;

typedef struct Vec_list_struct {
    int size;
    int maxsize;
    Vec ** list;
} Vec_list;

/*
 * Note that I at then end of an operation means inplace, as in one of the arguments is modified
 * these inplace operations return the target for convenience
 *
 * Other functions do not modify input arguments and instead return a new struct
 *
 * Most functions if given incompatible vectors ie dimensions don't match up will exit the program
 * equals is the exception and will merely return false
 *
 * To use existing arrays without copying the array with Vector math simply use Vec_createMan, but be careful
 */

/******************************************************/
// Vector Function Prototypes 
Vec* Vec_add(Vec *a, Vec *b);
Vec* Vec_addI(Vec *target, Vec *b);
Vec* Vec_convolveC(Vec* f, Vec *k);             // convolution of discrete function in 1 D crops to length of 1st argument
Vec* Vec_copy(Vec *v);                          // returns a copy of the given vector
Vec* Vec_create(float values[], int size);      // copies the values and returns a new vector
Vec* Vec_createE(int size);                     // returns a new vector with allocated space but does not initialize values
Vec* Vec_createGaussian(float sigma, int size); // create a discrete valued gaussian centered in the middle of the vector
Vec* Vec_createMan(float values[], int size);   // does not copy values, only uses a pointer to it, use with caution
Vec* Vec_createZeros(int size);                       // initializes a vector of zeros
Vec* Vec_crossProduct(Vec *a, Vec *b);
void Vec_destroy(Vec *target);                  // frees associated memory
float Vec_dotProduct(Vec *a, Vec *b);
short Vec_equals(Vec *a, Vec *b);               // are two vectors equal?
float Vec_L2norm(Vec *v);
float Vec_L2norm2(Vec *v);                      // squared L2 norm
Matrix * Vec_multiply(Vec *a, Vec *b);
Vec* Vec_normalize(Vec *v);                     // returns a normalized vector such that its L2 norm is 1
Vec* Vec_normalizeI(Vec *target);
void Vec_print(Vec *target);
Vec* Vec_scale(Vec *v, float scaleFactor);
Vec* Vec_scaleI(Vec *target, float scaleFactor);
Vec* Vec_subtract(Vec *a, Vec *b);
Vec* Vec_subtractI(Vec *target, Vec *b);
Matrix* Vec_toMatrix(Vec *v);
Vec* Vec_transpose(Vec* v);
Vec* Vec_transposeI(Vec *target);

// Matrix Function Prototypes
Matrix* Matrix_add(Matrix *A, Matrix *B);
Matrix* Matrix_addI(Matrix *target, Matrix *B);
Matrix* Matrix_convolveC(Matrix *F, Matrix *K);     // 2D convolution, crops to size of first argument, 2nd argument is kernel, must be odd
Matrix* Matrix_copy(Matrix *m);
Matrix* Matrix_create(int rows, int columns, ...);              // expects # of rows comma seperated float *s of length columns
Matrix* Matrix_createDiag(float *diag, int rows, int columns);  // creates matrix with diag on the diagonal of the matrix
Matrix* Matrix_createV(float *values, int rows, int columns);   // creates a matrix from a single vector of length rows * columns, assumed to be layed out [row1, row2, ...]
Matrix* Matrix_createE(int rows, int columns);                  // creates uninitialized matrix, pointers are all set up
Matrix* Matrix_createGaussian(float sigma, int size);
Matrix* Matrix_createIdentity(int rows, int columns);           // creates identity matrix
Matrix* Matrix_createMan(float **values, int rows, int columns);// good luck, you have to set up your pointers yourself and everything
Matrix* Matrix_createRBT2D(float dx, float dy, float dtheta);
Matrix* Matrix_createZeros(int rows, int columns);              // initializes all elements to zero
void Matrix_destroy(Matrix *m);
float Matrix_det(Matrix *m);                                    // matrix determinant 2x2 or 3x3 only
void Matrix_eigDecomp(Matrix *A, Matrix **ans);                           // performs eigenvalue decomposition 2x2 only places resulting matricies in ans first return is a diagonal matrix of eigenvalues, 2nd is a matrix whose columns are the associated eignevectors
short Matrix_equals(Matrix *A, Matrix *B);
Vec* Matrix_getRow(Matrix *m,int row);
Vec* Matrix_getColumn(Matrix *m, int column);
Matrix* Matrix_inverse(Matrix *m);                      // performs matrix inversion for 2x2 or 3x3 case only
Matrix* Matrix_multiplyAB(Matrix *A, Matrix *B);        // performs matrix multiplication with another matrix
Vec* Matrix_multiplyAV(Matrix *A, Vec *v);              // with a vector on the right and outputs a vector
Vec* Matrix_multiplyVB(Vec *v, Matrix *B);              // with a vector on the left
void Matrix_print(Matrix *m);
Matrix* Matrix_scale(Matrix *m, float scaleFactor);
Matrix* Matrix_scaleI(Matrix *target, float scaleFactor);
Matrix* Matrix_subtract(Matrix *A, Matrix *B);
Matrix* Matrix_subtractI(Matrix *target, Matrix *B);
Matrix* Matrix_transpose(Matrix *m);

/******************************************************/

void Vec_list_add(Vec_list *vlist, Vec *v);             // adds the selected vector from the list
void Vec_list_combineI(Vec_list **targetprt, Vec_list **blistprt); // appends blist to target and destroys blist
Vec_list* Vec_list_copy(Vec_list *that);
Vec_list* Vec_list_create(void);
Vec_list* Vec_list_createS(int initSize);
void Vec_list_destroy(Vec_list *vlist);                 // does not destroy the vectors contained within
void Vec_list_destroyR(Vec_list *vlist);                // destroys each vector it points to
Vec* V_getV(Vec_list *vlist, int index);                 // returns pointer to vector
short Vec_list_remove(Vec_list *vlist, int index);      // removes the vector from the selected index returns true if successful, does not destroy the removed vector
short Vec_list_removeR(Vec_list *vlist, int index);

#endif
